using com.abstractatech.gamification.tdjdosboxe;
using com.abstractatech.gamification.tdjdosboxe.HTML.Pages;
using ScriptCoreLib;
using ScriptCoreLib.Delegates;
using ScriptCoreLib.Extensions;
using ScriptCoreLib.JavaScript;
using ScriptCoreLib.JavaScript.Components;
using ScriptCoreLib.JavaScript.DOM;
using ScriptCoreLib.JavaScript.DOM.HTML;
using ScriptCoreLib.JavaScript.Extensions;
using System;
using System.Linq;
using System.Text;
using System.Xml.Linq;

using com.abstractatech.gamification.tdjdosboxe.Library;
using System.Windows.Forms;
using ScriptCoreLib.JavaScript.Runtime;
using com.abstractatech.gamification.tdjdosboxe.HTML.Images.FromAssets;
using ScriptCoreLib.ActionScript.flash.display;
using JellyworldExperiment.HardwareDetection;
using ScriptCoreLib.Ultra.WebService;

namespace com.abstractatech.gamification.tdjdosboxe
{


    public class MixedSensoryInput
    {
        public event Action onchange;

        bool __value;
        public bool value
        {
            get { return __value; }
            set
            {
                if (__value != value)
                {
                    __value = value;

                    if (onchange != null)
                        onchange();
                }
            }
        }
    }

    public class MixedSensory
    {
        public readonly MixedSensoryInput onkeyboard = new MixedSensoryInput();
        public readonly MixedSensoryInput onheadtracking = new MixedSensoryInput();
        public readonly MixedSensoryInput onaccelerometer = new MixedSensoryInput();

        public Keys KeyCode;


        public event Action onchange;

        public MixedSensory()
        {
            this.onkeyboard.onchange +=
                delegate
                {
                    if (onchange != null)
                        onchange();
                };

            this.onheadtracking.onchange +=
              delegate
              {
                  if (onchange != null)
                      onchange();
              };

            this.onaccelerometer.onchange +=
              delegate
              {
                  if (onchange != null)
                      onchange();
              };
        }

        public static implicit operator MixedSensory(Keys KeyCode)
        {
            return new MixedSensory { KeyCode = KeyCode };
        }

        public static implicit operator bool(MixedSensory e)
        {
            if (e.false_when_this_is_true != null)
                if (e.false_when_this_is_true)
                {
                    return false;
                }

            if (e.onkeyboard.value)
                return true;
            if (e.onheadtracking.value)
                return true;
            if (e.onaccelerometer.value)
                return true;

            return false;
        }

        public MixedSensory false_when_this_is_true;
    }

    public class MixedSensoryConfiguration
    {
        //sprite_keyup("" + (int)System.Windows.Forms.Keys.Up);
        //sprite_keydown("" + (int)System.Windows.Forms.Keys.Up);

        public event Action<string> keyup;
        public event Action<string> keydown;

        public readonly MixedSensory up = System.Windows.Forms.Keys.Up;
        public readonly MixedSensory left = System.Windows.Forms.Keys.Left;
        public readonly MixedSensory down = System.Windows.Forms.Keys.Down;
        public readonly MixedSensory right = System.Windows.Forms.Keys.Right;
        public readonly MixedSensory enter = new MixedSensory();

        public MixedSensoryConfiguration()
        {
            Action<MixedSensory> f =
                up =>
                {
                    var oldup = false;

                    up.onchange +=
                        delegate
                        {
                            bool newup = up;

                            if (oldup == newup)
                                return;

                            if (newup)
                                keydown("" + (int)up.KeyCode);
                            else
                                keyup("" + (int)up.KeyCode);

                            oldup = newup;
                        };
                };

            this.up.false_when_this_is_true = this.down;

            f(this.up);
            f(this.left);
            f(this.right);
            f(this.down);
            f(this.enter);
        }
    }

    /// <summary>
    /// Your client side code running inside a web browser as JavaScript.
    /// </summary>
    public sealed class Application
    {
        com.abstractatech.gamification.tdjdosboxe.Assets.Publish ref0;

        public readonly ApplicationWebService service = new ApplicationWebService();

        public readonly MixedSensoryConfiguration input = new MixedSensoryConfiguration();

        /// <summary>
        /// This is a javascript application.
        /// </summary>
        /// <param name="page">HTML document rendered by the web server which can now be enhanced.</param>
        public Application(IApp page)
        {

            var borders = Native.Document.body;

            page.init.Hide();

            var location = "" + Native.Document.location;

            page.location.innerText = location;
            page.location.href = location;

            Native.Document.title = "Test Drive: jDOSBox Edition";

            var disable_ondeviceorientation = false;

            var errortimer = new ScriptCoreLib.JavaScript.Runtime.Timer(
                delegate
                {
                    disable_ondeviceorientation = true;
                    borders.style.borderRightColor = "rgba(255, 0, 0, 0.2)";
                    borders.style.borderLeftColor = "rgba(255, 0, 0, 0.2)";
                    borders.style.borderBottomColor = "rgba(255, 0, 0, 0.2)";
                    borders.style.borderTopColor = "rgba(255, 0, 0, 0.2)";
                }
            );

            #region newicon
            Action newicon = delegate
            {
                Enumerable.AsEnumerable(
                    Native.Document.getElementsByTagName("link")
                ).ToList().WithEach(
                    e =>
                    {
                        var link = (IHTMLLink)e;

                        if (link.rel == "icon")
                        {
                            if (link.type == "image/png")
                            {

                                link.href = new Preview().src;
                            }
                            else
                            {
                                link.Orphanize();
                            }
                        }
                    }
                );

            };

            newicon();
            #endregion


            dynamic ss = borders.style;

            ss.webkitTransition = "all 0.3s linear";


            #region FlashGreen
            Action<Action> FlashGreen =
                yield =>
                {
                    borders.style.borderRightColor = "rgba(0, 255, 0, 0.5)";
                    borders.style.borderLeftColor = "rgba(0, 255, 0, 0.5)";
                    borders.style.borderBottomColor = "rgba(0, 255, 0, 0.5)";
                    borders.style.borderTopColor = "rgba(0, 255, 0, 0.5)";

                    new ScriptCoreLib.JavaScript.Runtime.Timer(
                        delegate
                        {
                            borders.style.borderRightColor = "rgba(255, 255, 0, 0)";
                            borders.style.borderLeftColor = "rgba(255, 255, 0, 0)";
                            borders.style.borderBottomColor = "rgba(255, 255, 0, 0)";
                            borders.style.borderTopColor = "rgba(255, 255, 0, 0)";

                            yield();
                        }
                    ).StartTimeout(500);
                };
            #endregion


            Action<string> sprite_keyup = delegate { };
            Action<string> sprite_keydown = delegate { };

            input.keydown +=
                KeyCode =>
                {
                    sprite_keydown(KeyCode);
                };

            input.keyup +=
                KeyCode =>
                {
                    sprite_keyup(KeyCode);
                };

            Action<Action<string>> __GetPixels = delegate { };

            var choices = new ScriptCoreLib.JavaScript.Runtime.Timer();

            choices.Tick +=
                delegate
                {
                    if (choices.Counter % 2 == 0)
                        borders.style.borderBottomColor = "rgba(255, 255, 0, 0.2)";
                    else
                        borders.style.borderBottomColor = "rgba(255, 255, 0, 0.0)";



                };
            FlashGreen(
                delegate
                {
                    choices.StartInterval(300);
                }
            );


            #region Android
            page.Android.onclick +=
                delegate
                {
                    choices.Stop();
                    page.content.Clear();

                    FlashGreen(
                       delegate
                       {
                           page.instructions.Orphanize();

                           var vk_enter = false;

                           var goup = false;
                           var goright = false;
                           var godown = false;
                           var goleft = false;

                           var zyx = new IHTMLPre { innerText = "" }.AttachToDocument();

                           Native.Document.body.ontouchstart +=
                               e =>
                               {

                                   e.preventDefault();

                                   FlashGreen(delegate { });

                                   vk_enter = true;
                                   disable_ondeviceorientation = true;
                               };

                           #region FlashGreen onorientationchange
                           Native.window.onorientationchange +=
                               delegate
                               {
                                   disable_ondeviceorientation = true;

                                   FlashGreen(
                                       delegate
                                       {
                                           disable_ondeviceorientation = false;
                                       }
                                   );
                               };
                           #endregion



                           #region ondeviceorientation
                           Native.window.ondeviceorientation +=
                               e =>
                               {

                                   #region desktop chrome misreports?
                                   // Uncaught ReferenceError: alpha is not defined 
                                   if ("this.alpha == null".js<bool>(e))
                                   {
                                       return;
                                   }
                                   #endregion



                                   var orientation = Native.window.orientation;

                                   e.preventDefault();
                                   e.stopPropagation();

                                   zyx.innerText = new
                                   {
                                       alpha = (int)e.alpha,
                                       beta = (int)e.beta,
                                       gamma = (int)e.gamma,
                                       orientation
                                   }.ToString();


                                   if (disable_ondeviceorientation)
                                       return;

                                   borders.style.borderRightColor = "rgba(255, 255, 255, 0.0)";
                                   borders.style.borderLeftColor = "rgba(255, 255, 255, 0.0)";
                                   borders.style.borderBottomColor = "rgba(255, 255, 255, 0.0)";
                                   borders.style.borderTopColor = "rgba(255, 255, 255, 0.0)";

                                   goup = false;
                                   goright = false;
                                   godown = false;
                                   goleft = false;

                                   #region decide
                                   Action<double, double> decide =
                                       (movementY, movementZ) =>
                                       {


                                           if (movementY < 35)
                                           {
                                               // device is on the table. discard the sensor.
                                               if (movementY > 5)
                                               {
                                                   borders.style.borderTopColor = "rgba(0, 255, 0, 1.0)";
                                                   goup = true;
                                               }
                                           }
                                           //else if (movementY > 33)
                                           else if (movementY > 60)
                                           {
                                               borders.style.borderBottomColor = "rgba(255, 0, 0, 1)";
                                               godown = true;
                                           }

                                           if (movementZ < -10)
                                           {
                                               goleft = true;
                                               borders.style.borderLeftColor = "rgba(255, 255, 0, 0.5)";
                                           }
                                           else if (movementZ > 10)
                                           {
                                               goright = true;
                                               borders.style.borderRightColor = "rgba(255, 255, 0, 0.5)";

                                           }
                                       };
                                   #endregion

                                   //if (movementY < -2)
                                   if (orientation == 0)
                                   {
                                       //  which is it 0 or 180

                                       var movementY = e.beta;
                                       var movementZ = e.gamma;

                                       if (e.beta < 0)
                                       {
                                           // up side down!
                                           movementY = -e.beta;
                                           //movementZ = -e.gamma;
                                       }

                                       decide(movementY, movementZ);
                                   }
                                   else if (orientation == 180)
                                   {
                                       // when is 180 reported?

                                       var movementY = -e.beta;
                                       var movementZ = -e.gamma;


                                       decide(movementY, movementZ);

                                   }
                                   #region landscape
                                   else if (orientation == -90)
                                   {
                                       var movementY = e.gamma;
                                       var movementZ = -e.beta;

                                       decide(movementY, movementZ);

                                   }
                                   else if (orientation == 90)
                                   {
                                       var movementY = -e.gamma;
                                       var movementZ = e.beta;

                                       decide(movementY, movementZ);

                                   }
                                   #endregion

                               };
                           #endregion

                           var id = new Random().Next();
                           var frame = 0;

                           #region loop
                           Action loop = null;

                           //var zdx = 0;
                           //var zdy = 0;
                           var zgoup = false;
                           var zgoright = false;
                           var zgodown = false;
                           var zgoleft = false;

                           loop = delegate
                           {
                               frame++;

                               if (vk_enter)
                               {
                                   vk_enter = false;
                                   goup = true;
                                   goleft = true;
                                   goright = true;
                                   godown = true;
                               }

                               if (frame > 1)
                                   if (zgoup == goup)
                                       if (zgoright == goright)
                                           if (zgodown == godown)
                                               if (zgoleft == goleft)
                                               {
                                                   // check again
                                                   Native.window.requestAnimationFrame += loop;

                                                   return;
                                               }

                               //zdx = dx;
                               //zdy = dy;

                               zgoup = goup;
                               zgoright = goright;
                               zgodown = godown;
                               zgoleft = goleft;

                               //dx = 0;
                               //dy = 0;

                               errortimer.Stop();
                               errortimer.StartTimeout(2000);

                               service.AtFrame(
                                   "" + id,
                                   "" + frame,


                                   goleft: "" + System.Convert.ToInt32(zgoleft),
                                   goup: "" + System.Convert.ToInt32(zgoup),
                                   goright: "" + System.Convert.ToInt32(zgoright),
                                   godown: "" + System.Convert.ToInt32(zgodown),

                                   yield:
                                       delegate
                                       {
                                           disable_ondeviceorientation = false;
                                           errortimer.Stop();

                                           Native.window.requestAnimationFrame += loop;
                                       }
                               );


                           };

                           Native.window.requestAnimationFrame += loop;
                           #endregion
                       }
                    );

                };
            #endregion

            var loadingplugin = new ScriptCoreLib.JavaScript.Runtime.Timer();

            loadingplugin.Tick +=
                delegate
                {
                    if (loadingplugin.Counter % 2 == 0)
                        borders.style.borderTopColor = "rgba(255, 0, 0, 0.6)";
                    else
                        borders.style.borderTopColor = "rgba(255, 0, 0, 0.0)";

                };


            #region Laptop
            Action AtLaptop =
                delegate
                {
                    page.init.Show();
                    page.initText.innerText = "Loading applet...";

                    choices.Stop();
                    page.content.Clear();



                    // style as dynamic?
                    //ss.border.right.color = "";



                    // Initialize ApplicationApplet
                    var applet = new ApplicationApplet();

                    page.initText.innerText = "Loading applet... setting params...";
                    applet.AsAppletParamDynamicObject().With(
                        (dynamic p) =>
                        {
                            // type MOUNT -u followed by the drives letter.
                            p.param1 = "imgmount e "
                                + location.TakeUntilLastIfAny("/")
                                + "/" + new Goodies.disk3MB().Source
                                + " -size 512,8,2,384";

                            p.param2 = "e:";
                            p.param3 = "cd td2";
                            p.param4 = "TD2EGA";
                            p.param5 = "cls";
                            p.param6 = "reboot";
                        }
                    );

                    borders.style.borderRightColor = "rgba(255, 255, 0, 0)";
                    borders.style.borderLeftColor = "rgba(255, 255, 0, 0)";
                    borders.style.borderBottomColor = "rgba(255, 255, 0, 0)";
                    borders.style.borderTopColor = "rgba(255, 255, 0, 0)";


                    // chrome might want you to right click on it?
                    applet.ToHTMLElement().style.height = "96px";


                    loadingplugin.StartInterval(300);


                    page.init.Show();

                    applet.WhenReady(
                        delegate
                        {
                            page.initText.innerText = "Loading applet... done!";

                            page.init.Hide();
                            loadingplugin.Stop();

                            page.instructions.Orphanize();


                            applet.ToHTMLElement().style.height = ApplicationApplet.DefaultHeight + "px";

                            var borderTopExtraAvailable = true;
                            var borderTopExtra = new ScriptCoreLib.JavaScript.Runtime.Timer(
                                delegate
                                {
                                    borders.style.borderTopColor = "rgba(0, 255, 0, 0.2)";
                                }
                            );


                            #region __KeyDown
                            applet.__KeyDown +=
                                (keyChar, keyCode) =>
                                {
                                    Console.WriteLine(
                                        "__KeyDown " + new { keyChar, keyCode }
                                    );
                                    var iKeyCode = (Keys)int.Parse(keyCode);



                                    if (iKeyCode == Keys.Left)
                                        borders.style.borderLeftColor = "rgba(255, 255, 0, 0.2)";
                                    else if (iKeyCode == Keys.Right)
                                        borders.style.borderRightColor = "rgba(255, 255, 0, 0.2)";
                                    else if (iKeyCode == Keys.Up)
                                    {
                                        if (borderTopExtraAvailable)
                                        {
                                            borders.style.borderTopColor = "rgba(0, 255, 0, 1.0)";
                                            // what about repeats?
                                            borderTopExtraAvailable = false;
                                            borderTopExtra.StartTimeout(300);
                                        }
                                    }
                                    else if (iKeyCode == Keys.Down)
                                        borders.style.borderBottomColor = "rgba(255, 0, 0, 1.0)";
                                };

                            applet.__KeyUp +=
                             (keyChar, keyCode) =>
                             {
                                 var iKeyCode = (Keys)int.Parse(keyCode);

                                 if (iKeyCode == Keys.Left)
                                     borders.style.borderLeftColor = "rgba(0, 255, 0, 0)";
                                 else if (iKeyCode == Keys.Right)
                                     borders.style.borderRightColor = "rgba(0, 255, 0, 0)";
                                 else if (iKeyCode == Keys.Up)
                                 {
                                     borderTopExtra.Stop();
                                     borderTopExtraAvailable = true;
                                     borders.style.borderTopColor = "rgba(0, 255, 0, 0)";
                                 }
                                 else if (iKeyCode == Keys.Down)
                                     borders.style.borderBottomColor = "rgba(0, 255, 0, 0)";
                                 else if (iKeyCode == Keys.F11)
                                     applet.__MainApplet_fullscreen();
                                 else if (iKeyCode == Keys.LineFeed)
                                     FlashGreen(delegate { });





                                 Console.WriteLine(
                                     "__KeyUp " + new { keyChar, keyCode }
                                 );
                             };
                            #endregion

                            FlashGreen(
                                delegate
                                {
                                    // ready for sensory input

                                    page.play.onclick +=
                                        delegate
                                        {
                                            applet.__MainApplet_fullscreen();
                                        };

                                    var glowtimer = new ScriptCoreLib.JavaScript.Runtime.Timer(
                                        delegate
                                        {

                                            applet.__GetPixels(
                                                (boxShadow) =>
                                                {
                                                    //Console.WriteLine(new { boxShadow });
                                                    applet.ToHTMLElement().style.boxShadow = boxShadow;

                                                }
                                            );
                                        }
                                    );


                                    // chrome app?
                                    glowtimer.StartInterval(1000 / 15);



                                    sprite_keyup =
                                   x =>
                                   {
                                       applet.__MainApplet_keyReleased(
                                           x, x, Console.WriteLine
                                       );
                                   };

                                    sprite_keydown =
                                      x =>
                                      {
                                          applet.__MainApplet_keyPressed(
                                              x, x, Console.WriteLine
                                          );
                                      };

                                    var status = new IHTMLPre { innerText = "" }.AttachToDocument();
                                    //var onmessage = new IHTMLPre { innerText = "" }.AttachToDocument();
                                    status.style.color = JSColor.Gray;

                                    #region EventSource
                                    new EventSource("/event-stream").With(
                                      s =>
                                      {
                                          //s.status =
                                          s["status"] =
                                              e =>
                                              {
                                                  //Native.Document.title = "" + e.data;
                                                  status.innerText = "" + e.data;
                                              };



                                          s.onerror +=
                                              e =>
                                              {
                                                  //Native.Document.title = "error";
                                                  //errortimer.StartTimeout(500);
                                              };

                                          #region onmessage
                                          s.onmessage +=
                                              e =>
                                              {
                                                  errortimer.Stop();
                                                  status.innerText = "..";

                                                  var xml = "" + e.data;
                                                  var data = XElement.Parse(xml);

                                                  //long
                                                  //    last_ms = long.Parse(data.Attribute("last_ms").Value),

                                                  //    x = long.Parse(data.Attribute("x").Value),
                                                  //    y = long.Parse(data.Attribute("y").Value);

                                                  bool
                                                      goleft = 0 < long.Parse(data.Attribute("goleft").Value),
                                                      goup = 0 < long.Parse(data.Attribute("goup").Value),
                                                      goright = 0 < long.Parse(data.Attribute("goright").Value),
                                                      godown = 0 < long.Parse(data.Attribute("godown").Value);

                                                  status.innerText = "";


                                                  //onmessage.innerText = "" +
                                                  //    new
                                                  //    {
                                                  //        //last_ms,
                                                  //        //x,
                                                  //        //y,
                                                  //        goleft,
                                                  //        goup,
                                                  //        goright,
                                                  //        godown
                                                  //    };

                                                  if (goup)
                                                      if (godown)
                                                          if (goleft)
                                                              if (goright)
                                                              {
                                                                  sprite_keydown("" + (int)System.Windows.Forms.Keys.LineFeed);
                                                                  sprite_keyup("" + (int)System.Windows.Forms.Keys.LineFeed);

                                                                  return;
                                                              }

                                                  //sprite_keyup("" + (int)System.Windows.Forms.Keys.Up);
                                                  //sprite_keyup("" + (int)System.Windows.Forms.Keys.Down);
                                                  //sprite_keyup("" + (int)System.Windows.Forms.Keys.Left);
                                                  //sprite_keyup("" + (int)System.Windows.Forms.Keys.Right);

                                                  input.up.onaccelerometer.value = goup;
                                                  input.left.onaccelerometer.value = goleft;
                                                  input.right.onaccelerometer.value = goright;
                                                  input.down.onaccelerometer.value = godown;


                                                  //if (goup)
                                                  //{
                                                  //    //sprite_keyup("" + (int)System.Windows.Forms.Keys.Up);
                                                  //    //sprite_keydown("" + (int)System.Windows.Forms.Keys.Up);
                                                  //}
                                                  //else if (godown)
                                                  //{
                                                  //    sprite_keydown("" + (int)System.Windows.Forms.Keys.Down);
                                                  //}

                                                  //if (goleft)
                                                  //{
                                                  //    sprite_keydown("" + (int)System.Windows.Forms.Keys.Left);
                                                  //}
                                                  //else if (goright)
                                                  //{
                                                  //    sprite_keydown("" + (int)System.Windows.Forms.Keys.Right);
                                                  //}
                                              };
                                          #endregion

                                      }
                                    );
                                    #endregion


                                }
                            );
                        }
                    );

                    // http://productforums.google.com/forum/#!topic/chrome/vjjBEatjrMs
                    page.initText.innerText = "Loading applet...";
                    applet.AttachAppletTo(page.content);
                };

            #endregion


            page.Laptop.onclick +=
                delegate
                {
                    AtLaptop();

                };

            #region CameraLaptop
            page.CameraLaptop.onclick +=
                  delegate
                  {
                      page.init.Show();
                      loadingplugin.StartInterval(300);
                      choices.Stop();
                      borders.style.borderBottomColor = "rgba(255, 255, 0, 0)";
                      page.content.Clear();

                      var shadow = new IHTMLElement("shadow");

                      shadow.style.position = IStyle.PositionEnum.absolute;
                      shadow.style.left = "0px";
                      shadow.style.top = "0px";
                      shadow.style.right = "0px";
                      shadow.style.bottom = "0px";
                      shadow.style.overflow = IStyle.OverflowEnum.hidden;

                      //Native.Document.body.parentNode.insertBefore(shadow, Native.Document.body);
                      Native.Document.body.parentNode.appendChild(shadow);




                      var wsprite = new WithCamera.__Sprite();

                      Action WhenReady = null;

                      Action<double, double, double> onheadmovement =
                          delegate
                          {

                          };
                      var status = new IHTMLCode { innerText = "" }.AttachToDocument();
                      //var onmessage = new IHTMLPre { innerText = "" }.AttachToDocument();
                      status.style.color = JSColor.Gray;



                      var treshold_left = .32;
                      var treshold_right = .32;

                      var treshold_reset = new ScriptCoreLib.JavaScript.Runtime.Timer(
                          delegate
                          {
                              treshold_left = .32;
                              treshold_right = .32;
                          }
                      );

                      WhenReady =
                          delegate
                          {
                              WhenReady = delegate { };

                              page.init.Hide();

                              Native.Document.body.style.zIndex = 1000;
                              shadow.style.zIndex = 100;

                              FlashGreen(
                                  delegate
                                  {
                                      onheadmovement =
                                          (LeftPercentage, RightPercentage, HeightPercentage) =>
                                          {
                                              bool
                                                goleft = treshold_left > LeftPercentage,
                                                goright = treshold_right > RightPercentage,

                                                goup = 0.35 < HeightPercentage,
                                                godown = 0.31 > HeightPercentage;

                                              status.innerText =
                                                  new
                                                  {
                                                      treshold_left = treshold_left.ToPercentString(),
                                                      LeftPercentage = LeftPercentage.ToPercentString(),
                                                      HeightPercentage = HeightPercentage.ToPercentString(),
                                                      RightPercentage = RightPercentage.ToPercentString(),
                                                      treshold_right = treshold_right.ToPercentString(),
                                                  }.ToString();

                                              if (!treshold_reset.IsAlive)
                                                  if (goleft)
                                                  {
                                                      // allow to correct
                                                      treshold_left = .32;
                                                      treshold_right = 0.5;
                                                      treshold_reset.StartTimeout(500);

                                                  }

                                              if (!treshold_reset.IsAlive)
                                                  if (goright)
                                                  {
                                                      // allow to correct
                                                      treshold_right = .32;
                                                      treshold_left = 0.5;
                                                      treshold_reset.StartTimeout(500);
                                                  }

                                              input.up.onheadtracking.value = goup;
                                              input.down.onheadtracking.value = godown;
                                              input.left.onheadtracking.value = goleft;
                                              input.right.onheadtracking.value = goright;
                                          };

                                      AtLaptop();
                                  }
                              );
                          };

                      wsprite.ToTransparentSprite();

                      //wsprite.AutoSizeSpriteTo(shadow);
                      wsprite.AttachSpriteTo(shadow);

                      wsprite.ToHTMLElement().With(
                          e =>
                          {
                              e.style.position = IStyle.PositionEnum.@static;
                              e.style.left = "0px";
                              e.style.top = "0px";
                              e.style.width = "100%";
                              e.style.height = "100%";
                          }
                      );

                      //AtLaptop();

                      wsprite.InitializeContent();



                      wsprite.AverageChanged +=
                          (Left, Top, Width, Height) =>
                          {
                              WhenReady();

                              var Right = int.Parse(Left) + int.Parse(Width);
                              var Bottom = int.Parse(Top) + int.Parse(Height);

                              var LeftPercent = (double)int.Parse(Left) / (Native.window.Width - int.Parse(Width));
                              var RightPercent = ((double)(Native.window.Width - Right) / (Native.window.Width - int.Parse(Width)));
                              var HeightPercent = (double)int.Parse(Height) / Native.window.Height;

                              onheadmovement(
                                  LeftPercent,
                                  RightPercent,
                                  HeightPercent
                              );
                          };
                  };
            #endregion


            #region __HardwareDetection

            // Initialize ApplicationSprite
            sprite.AttachSpriteToDocument().style.SetSize(1, 1);

            sprite.FoundCamera +=
                delegate
                {
                    page.CameraLaptop.disabled = false;
                };

            sprite.InitializeContent();
            #endregion



            Native.window.onfocus +=
                delegate
                {
                    FlashGreen(delegate { });
                };


            #region onkeydown
            Native.Document.body.onkeydown +=
             e =>
             {
                 e.preventDefault();
                 e.stopPropagation();

                 //sprite_keyup("" + (int)System.Windows.Forms.Keys.Up);
                 //sprite_keydown("" + (int)System.Windows.Forms.Keys.Up);

                 if ((Keys)e.KeyCode == Keys.Up)
                 {
                     input.up.onkeyboard.value = true;
                     return;
                 }
                 if ((Keys)e.KeyCode == Keys.Down)
                 {
                     input.down.onkeyboard.value = true;
                     return;
                 }
                 if ((Keys)e.KeyCode == Keys.Left)
                 {
                     input.left.onkeyboard.value = true;
                     return;
                 }
                 if ((Keys)e.KeyCode == Keys.Right)
                 {
                     input.right.onkeyboard.value = true;
                     return;
                 }
                 int KeyCode = e.KeyCode;
                 if (KeyCode == 13)
                     KeyCode = 10;
                 int KeyChar = KeyCode;




                 sprite_keydown("" + KeyCode);

             };

            Native.Document.body.onkeyup +=
                e =>
                {
                    e.preventDefault();
                    e.stopPropagation();



                    if ((Keys)e.KeyCode == Keys.Up)
                    {
                        input.up.onkeyboard.value = false;
                        return;
                    }
                    if ((Keys)e.KeyCode == Keys.Down)
                    {
                        input.down.onkeyboard.value = false;
                        return;
                    }
                    if ((Keys)e.KeyCode == Keys.Left)
                    {
                        input.left.onkeyboard.value = false;
                        return;
                    }
                    if ((Keys)e.KeyCode == Keys.Right)
                    {
                        input.right.onkeyboard.value = false;
                        return;
                    }
                    int KeyCode = e.KeyCode;

                    if (KeyCode == 13)
                        KeyCode = 10;



                    int KeyChar = KeyCode;



                    sprite_keyup("" + KeyCode);
                };
            #endregion

        }


        #region Application_HardwareDetection_Sprite
        [Obsolete("Temporary workaround to enable multiple apps.")]
        public sealed class __Sprite : Sprite, IApplicationSprite
        {
            public event Action FoundMutedCamera;
            public event Action FoundUnmutedCamera;

            public event Action FoundCamera;
            public event Action LookingForCamera;

            public void RaiseLookingForCamera()
            {
                if (LookingForCamera != null)
                    LookingForCamera();
            }


            public void RaiseFoundCamera()
            {
                if (FoundCamera != null)
                    FoundCamera();
            }

            public void RaiseFoundMutedCamera()
            {
                if (FoundMutedCamera != null)
                    FoundMutedCamera();
            }

            public void RaiseFoundUnmutedCamera()
            {
                if (FoundUnmutedCamera != null)
                    FoundUnmutedCamera();
            }

            public void InitializeContent()
            {
                this.InternalInitializeContent();
            }
        }

        public readonly __Sprite sprite = new __Sprite();
        #endregion
    }



    public static class X
    {
        public static string ToPercentString(this double body)
        {
            return System.Convert.ToInt32(100 * body) + "%";
        }

        public static T js<T>(this string body, object that = null)
        {
            return (T)new IFunction("return " + body + ";").apply(that);
        }
    }








    public static class DownloadSDKFunction
    {
        // - The digital signature of the object did not verify.

        public static void DownloadSDK(WebServiceHandler h)
        {
            const string _download = "/download/";

            var path = h.Context.Request.Path;

            if (path == "/crx")
            {
                // https://code.google.com/p/chromium/issues/detail?id=128748

                h.Context.Response.Redirect("/download/foo.crx");
                h.CompleteRequest();
                return;
            }

            var p = new com.abstractatech.gamification.tdjdosboxe.Assets.Publish();
            //var p2 = new WithClickOnceLANLauncher.Assets.Publish2();

            if (path == "/download")
            {
                //I/System.Console( 3016):        at ScriptCoreLibJava.BCLImplementation.System.Collections.Generic.__Dictionary_2___KeyCollection.System_Collections_ICollection_get_Count(__Dictionary_2___KeyCollection.java:83)
                //I/System.Console( 3016):        at ScriptCoreLib.Extensions.IEnumerableExtensions.AsEnumerable(IEnumerableExtensions.java:25)
                //I/System.Console( 3016):        at WithClickOnceLANLauncher.DownloadSDKFunction.DownloadSDK(DownloadSDKFunction.java:73)
                //I/System.Console( 3016):        at WithClickOnceLANLauncher.ApplicationWebService.DownloadSDK(ApplicationWebService.java:44)

                var key = p.Keys.ICollectionAsEnumerable().Select(k => p[(string)k]).First(k => k.EndsWith(".application")).SkipUntilLastIfAny("/");

                Console.WriteLine(
                    new
                    {
                        key
                    }
                );

                h.Context.Response.Redirect("/download/" + key);
                h.CompleteRequest();
                return;
            }



            //if (path == "/download/jsc-web-installer.exe")
            //{
            //    // http://msdn.microsoft.com/en-us/library/h4k032e1.aspx
            //    // is chrome happier if we rename it?
            //    path = "/download/setup.exe";
            //}

            if (path == "/download/")
            {
                var href = "http://www.jsc-solutions.net/download/jsc-web-installer.exe";

                var html = @"
                    <meta http-equiv='Refresh' target='_top' content='1;url=" + href + @"' />

                    
                    <center>
                    
                    <br />
                    <br />
                    <br />

<a href='" + href + @"'>Thank you for downloading JSC!</a>
                     
                    <br />
                    <br />
                    <br />
                    <br />
                    <br />

<div><i>Note that recent versions of <b>Google Chrome</b> may need additional time to verify.</i></div>                  
                                       
                                       </center>";

                h.Context.Response.ContentType = "text/html";

                var bytes = Encoding.UTF8.GetBytes(html);
                h.Context.Response.OutputStream.Write(bytes, 0, bytes.Length);
                h.CompleteRequest();
                return;
            }


            // we will compare the win32 relative paths here...
            var publish = path.SkipUntilOrEmpty("/download/").Replace("/", @"\");

            // 	- Exception reading manifest from http://192.168.1.100:24257/#/download/Application%20Files/WithClickOnceLANLauncherClient_1_0_0_2/WithClickOnceLANLauncherClient.exe.manifest: the manifest may not be valid or the file could not be opened.
            // did publish work and were it compiled into AssetsLibrary correctly?

            if (p.ContainsKey(publish))
            {
                var f = p[publish];


                var ext = "." + f.SkipUntilLastOrEmpty(".").ToLower();

                // http://en.wikipedia.org/wiki/Mime_type
                // http://msdn.microsoft.com/en-us/library/ms228998.aspx

                var ContentType = "application/octet-stream";

                if (ext == ".application")
                {
                    ContentType = "application/x-ms-application";
                }
                else if (ext == ".manifest")
                {
                    ContentType = "application/x-ms-manifest";
                }
                else if (ext == ".htm")
                {
                    ContentType = "text/html";
                }
                else if (ext == ".crx")
                {
                    // http://feedback.livereload.com/knowledgebase/articles/85889-chrome-extensions-apps-and-user-scripts-cannot
                    // http://stackoverflow.com/questions/12049366/re-enabling-extension-installs

                    ContentType = "application/x-chrome-extension";
                    // Resource interpreted as Document but transferred with MIME type application/x-chrome-extension: "http://192.168.1.106:16507/download/foo.crx".

                    //h.Context.Response.AddHeader(
                    //    "Content-Disposition", "attachment; filename=\"xfoo.crx\"");


                }


                h.Context.Response.ContentType = ContentType;




                DownloadSDKFile(h, f);


            }




            return;
        }

        private static void DownloadSDKFile(WebServiceHandler h, string fpath, string folder = "/download")
        {
            // https://sites.google.com/a/jsc-solutions.net/backlog/knowledge-base/2013/201306/20130605-lan-clickonce

            Console.WriteLine("download: " + fpath);

            if (fpath.EndsWith(".application"))
            {
                var bytes_application = System.IO.File.ReadAllText(fpath);

                var HostUri = new
                {
                    Host = h.Context.Request.Headers["Host"].TakeUntilIfAny(":"),
                    Port = h.Context.Request.Headers["Host"].SkipUntilIfAny(":")
                };

                var x = bytes_application.Replace(
                    "127.0.0.1:8181",

                    // change path by adding a sub folder
                    HostUri.Host + ":" + HostUri.Port + folder
                );
                Console.WriteLine(x);
                h.Context.Response.Write(x);
                h.CompleteRequest();
                return;
            }


            var bytes = System.IO.File.ReadAllBytes(fpath);
            h.Context.Response.OutputStream.Write(bytes, 0, bytes.Length);
            h.CompleteRequest();
        }
    }

}
